From: Cosimo Cecchi Date: Wed, 20 Jun 2012 21:23:34 +0000 (-0400) Subject: mount-operation: implement org.Gtk.MountOperationHandler X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~6434 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=6b5f9a647a;p=gnome-shell.git mount-operation: implement org.Gtk.MountOperationHandler Use the ShellMountOperation dialogs we have to implement a DBus API allowing other processes to display them. Since GtkMountOperation now tries to call into our DBus implementation, every application that uses a GtkMountOperation will gain integration with our shell dialogs (but will still handle the actual communication with GVfs). https://bugzilla.gnome.org/show_bug.cgi?id=678516 --- diff --git a/js/ui/main.js b/js/ui/main.js index bc5a3df30..b2ef94dd0 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -33,6 +33,7 @@ const WindowAttentionHandler = imports.ui.windowAttentionHandler; const Scripting = imports.ui.scripting; const SessionMode = imports.ui.sessionMode; const ShellDBus = imports.ui.shellDBus; +const ShellMountOperation = imports.ui.shellMountOperation; const TelepathyClient = imports.ui.telepathyClient; const WindowManager = imports.ui.windowManager; const Magnifier = imports.ui.magnifier; @@ -60,6 +61,7 @@ let ctrlAltTabManager = null; let recorder = null; let sessionMode = null; let shellDBusService = null; +let shellMountOpDBusService = null; let modalCount = 0; let modalActorFocusStack = []; let uiGroup = null; @@ -150,6 +152,7 @@ function start() { sessionMode = new SessionMode.SessionMode(); shellDBusService = new ShellDBus.GnomeShell(); + shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will // also initialize ShellAppSystem first. ShellAppSystem diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js index dd4910a55..465891a5c 100644 --- a/js/ui/shellMountOperation.js +++ b/js/ui/shellMountOperation.js @@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Signals = imports.signals; const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Pango = imports.gi.Pango; const St = imports.gi.St; @@ -144,17 +145,17 @@ const ShellMountOperation = new Lang.Class({ this._dialog.open(); }, - _onAskPassword: function(op, message) { + _onAskPassword: function(op, message, defaultUser, defaultDomain, flags) { if (this._existingDialog) { this._dialog = this._existingDialog; this._dialog.reaskPassword(); } else { - this._dialog = new ShellMountPasswordDialog(message, this._gicon); + this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags); } this._dialogId = this._dialog.connect('response', Lang.bind(this, function(object, choice, password, remember) { - if (choice == '-1') { + if (choice == -1) { this.mountOp.reply(Gio.MountOperationResult.ABORTED); } else { if (remember) @@ -267,7 +268,7 @@ const ShellMountPasswordDialog = new Lang.Class({ Name: 'ShellMountPasswordDialog', Extends: ModalDialog.ModalDialog, - _init: function(message, gicon) { + _init: function(message, gicon, flags) { let strings = message.split('\n'); this.parent({ styleClass: 'prompt-dialog' }); @@ -326,10 +327,14 @@ const ShellMountPasswordDialog = new Lang.Class({ this._errorMessageLabel.hide(); this._messageBox.add(this._errorMessageLabel); - this._rememberChoice = new CheckBox.CheckBox(); - this._rememberChoice.getLabelActor().text = _("Remember Passphrase"); - this._rememberChoice.actor.checked = true; - this._messageBox.add(this._rememberChoice.actor); + if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) { + this._rememberChoice = new CheckBox.CheckBox(); + this._rememberChoice.getLabelActor().text = _("Remember Passphrase"); + this._rememberChoice.actor.checked = true; + this._messageBox.add(this._rememberChoice.actor); + } else { + this._rememberChoice = null; + } let buttons = [{ label: _("Cancel"), action: Lang.bind(this, this._onCancelButton), @@ -358,6 +363,7 @@ const ShellMountPasswordDialog = new Lang.Class({ _onEntryActivate: function() { this.emit('response', 1, this._passwordEntry.get_text(), + this._rememberChoice && this._rememberChoice.actor.checked); } }); @@ -455,3 +461,253 @@ const ShellProcessesDialog = new Lang.Class({ } }); Signals.addSignalMethods(ShellProcessesDialog.prototype); + +const GnomeShellMountOpIface = + + + + + + + + + + + + + + + + + + + + + + + + + + + + +; + +const ShellMountOperationType = { + NONE: 0, + ASK_PASSWORD: 1, + ASK_QUESTION: 2, + SHOW_PROCESSES: 3 +}; + +const GnomeShellMountOpHandler = new Lang.Class({ + Name: 'GnomeShellMountOpHandler', + + _init: function() { + this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this); + this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler'); + Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler', + Gio.BusNameOwnerFlags.REPLACE, null, null); + + this._dialog = null; + this._volumeMonitor = Gio.VolumeMonitor.get(); + + this._ensureEmptyRequest(); + }, + + _ensureEmptyRequest: function() { + this._currentId = null; + this._currentInvocation = null; + this._currentType = ShellMountOperationType.NONE; + }, + + _clearCurrentRequest: function(response, details) { + if (this._currentInvocation) { + this._currentInvocation.return_value( + GLib.Variant.new('(ua{sv})', [response, details])); + } + + this._ensureEmptyRequest(); + }, + + _setCurrentRequest: function(invocation, id, type) { + let oldId = this._currentId; + let oldType = this._currentType; + let requestId = id + '@' + invocation.get_sender(); + + this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {}); + + this._currentInvocation = invocation; + this._currentId = requestId; + this._currentType = type; + + if (this._dialog && (oldId == requestId) && (oldType == type)) + return true; + + return false; + }, + + _closeDialog: function() { + if (this._dialog) { + this._dialog.close(); + this._dialog = null; + } + }, + + _createGIcon: function(iconName) { + let realIconName = iconName ? iconName : 'drive-harddisk'; + return new Gio.ThemedIcon({ name: realIconName, + use_default_fallbacks: true }); + }, + + /** + * AskPassword: + * @id: an opaque ID identifying the object for which the operation is requested + * The ID must be unique in the context of the calling process. + * @message: the message to display + * @icon_name: the name of an icon to display + * @default_user: the default username for display + * @default_domain: the default domain for display + * @flags: a set of GAskPasswordFlags + * @response: a GMountOperationResult + * @response_details: a dictionary containing the response details as + * entered by the user. The dictionary MAY contain the following properties: + * - "password" -> (s): a password to be used to complete the mount operation + * - "password_save" -> (u): a GPasswordSave + * + * The dialog will stay visible until clients call the Close() method, or + * another dialog becomes visible. + * Calling AskPassword again for the same id will have the effect to clear + * the existing dialog and update it with a message indicating the previous + * attempt went wrong. + */ + AskPasswordAsync: function(params, invocation) { + let [id, message, iconName, defaultUser, defaultDomain, flags] = params; + + if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) { + this._dialog.reaskPassword(); + return; + } + + this._closeDialog(); + + this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags); + this._dialog.connect('response', Lang.bind(this, + function(object, choice, password, remember) { + let details = {}; + let response; + + if (choice == -1) { + response = Gio.MountOperationResult.ABORTED; + } else { + response = Gio.MountOperationResult.HANDLED; + + let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER; + details['password_save'] = GLib.Variant.new('u', passSave); + details['password'] = GLib.Variant.new('s', password); + } + + this._clearCurrentRequest(response, details); + })); + this._dialog.open(); + }, + + /** + * AskQuestion: + * @id: an opaque ID identifying the object for which the operation is requested + * The ID must be unique in the context of the calling process. + * @message: the message to display + * @icon_name: the name of an icon to display + * @choices: an array of choice strings + * GetResponse: + * @response: a GMountOperationResult + * @response_details: a dictionary containing the response details as + * entered by the user. The dictionary MAY contain the following properties: + * - "choice" -> (i): the chosen answer among the array of strings passed in + * + * The dialog will stay visible until clients call the Close() method, or + * another dialog becomes visible. + * Calling AskQuestion again for the same id will have the effect to clear + * update the dialog with the new question. + */ + AskQuestionAsync: function(params, invocation) { + let [id, message, iconName, choices] = params; + + if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) { + this._dialog.update(message, choices); + return; + } + + this._closeDialog(); + + this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message); + this._dialog.connect('response', Lang.bind(this, + function(object, choice) { + this._clearCurrentRequest(Gio.MountOperationResult.HANDLED, + { choice: GLib.Variant.new('i', choice) }); + })); + + this._dialog.update(message, choices); + this._dialog.open(); + }, + + /** + * ShowProcesses: + * @id: an opaque ID identifying the object for which the operation is requested + * The ID must be unique in the context of the calling process. + * @message: the message to display + * @icon_name: the name of an icon to display + * @application_pids: the PIDs of the applications to display + * @choices: an array of choice strings + * @response: a GMountOperationResult + * @response_details: a dictionary containing the response details as + * entered by the user. The dictionary MAY contain the following properties: + * - "choice" -> (i): the chosen answer among the array of strings passed in + * + * The dialog will stay visible until clients call the Close() method, or + * another dialog becomes visible. + * Calling ShowProcesses again for the same id will have the effect to clear + * the existing dialog and update it with the new message and the new list + * of processes. + */ + ShowProcessesAsync: function(params, invocation) { + let [id, message, iconName, applicationPids, choices] = params; + + if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) { + this._dialog.update(message, applicationPids, choices); + return; + } + + this._closeDialog(); + + this._dialog = new ShellProcessesDialog(this._createGIcon(iconName)); + this._dialog.connect('response', Lang.bind(this, + function(object, choice) { + let response; + let details = {}; + + if (choice == -1) { + response = Gio.MountOperationResult.ABORTED; + } else { + response = Gio.MountOperationResult.HANDLED; + details['choice'] = GLib.Variant.new('i', choice); + } + + this._clearCurrentRequest(response, details); + })); + + this._dialog.update(message, applicationPids, choices); + this._dialog.open(); + }, + + /** + * Close: + * + * Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses. + * If no dialog is open, does nothing. + */ + Close: function(params, invocation) { + this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {}); + this._closeDialog(); + } +});